package core.net;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;

import context.mm_context;
import log.mm_logger;
import utils.mm_parse;
import utils.mm_thread;

/**
 * @author dongyazhuo
 */
public class mm_http_client {

	private String TAG = mm_http_client.class.getSimpleName() + " : ";

	// private BasicCookieStore cookieStore = new BasicCookieStore();
	// HttpClients.createDefault();//
	private static HttpClient client = null;// HttpClients.createDefault();//
											// HttpClients.custom().setDefaultCookieStore(cookieStore).build();

	// 是否开启本地代理调试
	private static final boolean open_proxy_debug = false;

	static {

		SSLContext sslcontext = SSLContexts.createDefault();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
				sslcontext,
				new String[] { "TLSv1.1", "TLSv1.2", "TLSv1" },
				null,
				new HostnameVerifier() {

					@Override
					public boolean verify(String hostname, SSLSession session) {
						return true;
					}
				});
		HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {

			@Override
			public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
				return false;
			}
		};
		client = HttpClients.custom().setSSLSocketFactory(sslsf).setRetryHandler(retryHandler).build();

		// client = HttpClients.createDefault();
	}

	/**
	 * 绕过验证
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static SSLContext createIgnoreVerifySSL() {

		// 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(
					java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public void checkServerTrusted(
					java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};
		try {
			SSLContext sc = SSLContext.getInstance("SSLv3");
			sc.init(null, new TrustManager[] { trustManager }, null);
			return sc;
		} catch (Exception e) {
		}
		return null;

	}

	public void _set_header_default(HttpRequestBase request) {
		request.setHeader("Accept", "application/json, text/plain, */*");
		request.setHeader("Accept-Encoding", "gzip, deflate");
		request.setHeader("User-Agent",
				"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) 12306-electron/1.0.1 Chrome/59.0.3071.115 Electron/1.8.4 Safari/537.36");
		request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		request.setHeader(null);
	}

	public void reset_headers(HttpRequestBase request) {
		_set_header_default(request);
	}

	public void set_headers_referer(HttpRequestBase request, String referer) {
		request.setHeader("Referer", referer);
	}

	public void update_header(HttpRequestBase request, String name, String value) {
		request.removeHeaders(name);
		request.addHeader(name, value);
	}

	private URI get_url(Map<String, Object> url, Map<String, Object> data) {
		try {
			String req_url = mm_parse.parse_str(url.get("req_url"));
			boolean is_cdn = mm_parse.parse_bool(url.get("is_cdn"));

			String url_host = url.get("Host").toString();
			if (is_cdn) {
				String address = mm_context.instance().get_cdn();
				if (null != address) {
					url_host = address;
				}
			}
			if (null != url.get("is_test_cdn")) {
				url_host = url.get("test_cdn").toString();
			}
			URIBuilder builder = new URIBuilder(URI.create("https://" + url_host + req_url));
			if (null != data) {
				Set<String> set = data.keySet();
				for (String k : set) {
					Object v = data.get(k);
					builder.setParameter(k, v + "");
				}
			}
			return builder.build();
		} catch (Exception e) {
		}
		return null;

	}

	public synchronized Object send(Map<String, Object> url, Map<String, Object> param) {

		HttpHost proxy = open_proxy_debug ? new HttpHost("127.0.0.1", 8888) : null;

		boolean allow_redirects = false;
		boolean is_logger = mm_parse.parse_bool(url.get("is_logger"));
		String req_url = mm_parse.parse_str(url.get("req_url"));
		String referer = mm_parse.parse_str(url.get("Referer"));
		String req_type = mm_parse.parse_str(url.get("req_type"));
		double re_try = mm_parse.parse_num(url.get("re_try"));
		double s_time = mm_parse.parse_num(url.get("s_time"));
		double re_time = mm_parse.parse_num(url.get("re_time"));

		boolean not_decode = mm_parse.parse_bool(url.get("not_decode"));
		boolean is_json = mm_parse.parse_bool(url.get("is_json"));

		Map<String, String> error_data = new HashMap<String, String>();
		error_data.put("code", "99999");
		error_data.put("message", "重试次数达到上限");

		HttpRequestBase request = null;

		URI uri = get_url(url, param);

		if ("get".equals(req_type)) {
			request = new HttpGet(uri);
			reset_headers(request);
		} else {
			request = new HttpPost(uri);
			// request.addHeader("Content-Length", data.size() + "");
		}
		update_header(request, "Host", url.get("Host").toString());

		set_headers_referer(request, referer);

		RequestConfig config = RequestConfig.custom().setConnectTimeout(2000).setSocketTimeout(2000)
				.setConnectionRequestTimeout(2000)
				.setRedirectsEnabled(allow_redirects).setProxy(proxy)
				.build();
		request.setConfig(config);

		// proxies=self._proxies,
		// verify=False,
		// **kwargs

		for (int i = 0; i < re_try; i++) {
			try {
				mm_thread.sleep((long) s_time * 1000);
				// 请求
				HttpResponse response = client.execute(request);

				int status_code = response.getStatusLine().getStatusCode();
				if (status_code == 200 || status_code == 302) {
					if (response.getEntity().getContentType().getValue().equals("image/jpeg")) {
						return response.getEntity();
					}
					String r = EntityUtils.toString(response.getEntity());
					if (not_decode) {
						return r;
					}
					if (response.getEntity().getContent() != null) {
						if (is_logger) {
							mm_logger.i(TAG + "reponse： " + r);
						}
						if (is_json) {
							if (null == r || r.equals("")) {
								// mm_logger.i(TAG + "request fail：please check " +
								// response.getHeaders("Location")[0].getValue());
								continue;
							}
							return JSON.parseObject(r, HashMap.class);
						} else {
							return changeCharset(r, "UTF-8");
						}
					}
				} else {
					mm_thread.sleep((long) (re_time * 1000));
				}
			} catch (Exception e) {
				if (is_logger) {
					mm_logger.e(TAG + e.getMessage());
				}
			}
		}
		return error_data;
	}

	public String changeCharset(String str, String newCharset)
			throws UnsupportedEncodingException {
		if (str != null) {
			// 用旧的字符编码解码字符串。解码可能会出现异常。
			byte[] bs = str.getBytes();
			// 用新的字符编码生成字符串
			return new String(bs, newCharset);
		}
		return null;
	}

}
